package xiao.ze.demo.shiro;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.*;

/**
 * shiro配置类
 * Created by xiaozemaliya on 2017/4/3.
 */
@Configuration
public class ShiroConfiguration {

    /**
     * EhCacheManager，缓存管理，用户登陆成功后，把用户信息和权限信息缓存起来，
     * 然后每次用户请求时，放入用户的session中，如果不设置这个bean，每个请求都会查询一次数据库。
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public EhCacheManager ehCacheManager() {
        EhCacheManager ehcacheManager = new EhCacheManager();
        ehcacheManager.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml");
        return ehcacheManager;
    }

    @Bean
    public ModularRealmAuthenticator authenticator() {
        ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return modularRealmAuthenticator;
    }

    /**
     *    MD5加密配置
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcherMD5() {
        HashedCredentialsMatcher credentialsMatcherMD5 = new HashedCredentialsMatcher();
        credentialsMatcherMD5.setHashAlgorithmName("MD5");
        credentialsMatcherMD5.setHashIterations(1024);
        credentialsMatcherMD5.setStoredCredentialsHexEncoded(true);
        return credentialsMatcherMD5;
    }

    /**
     *    SHA1加密配置
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcherSHA1() {
        HashedCredentialsMatcher credentialsMatcherSHA1 = new HashedCredentialsMatcher();
        credentialsMatcherSHA1.setHashAlgorithmName("SHA1");
        credentialsMatcherSHA1.setHashIterations(1024);
        credentialsMatcherSHA1.setStoredCredentialsHexEncoded(true);
        return credentialsMatcherSHA1;
    }

    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public ShiroRealm shiroRealm() {
        ShiroRealm realm = new ShiroRealm();
        realm.setCredentialsMatcher(hashedCredentialsMatcherSHA1());
        return realm;
    }

    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public UsersRealm usersRealm() {
        UsersRealm realm = new UsersRealm();
        realm.setCredentialsMatcher(hashedCredentialsMatcherMD5());
        return realm;
    }

    /**
     * LifecycleBeanPostProcessor，这是个DestructionAwareBeanPostProcessor的子类，
     * 负责org.apache.shiro.util.Initializable类型bean的生命周期的，初始化和销毁。
     * 主要是AuthorizingRealm类的子类，以及EhCacheManager类。
     */
    @Bean(name="lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * SecurityManager，权限管理，这个类组合了登陆，登出，权限，session的处理，是个比较重要的类。
    */
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        securityManager.setCacheManager(ehCacheManager());
        securityManager.setAuthenticator(authenticator());

        List<Realm> list=new ArrayList<Realm>();
        list.add(usersRealm());
        list.add(shiroRealm());
        securityManager.setRealms(list);

        securityManager.setRememberMeManager(rememberMeManager());

        return securityManager;
    }

    /**
     * ShiroFilterFactoryBean，是个factorybean，为了生成ShiroFilter。
     * 它主要保持了三项数据，securityManager，filters，filterChainDefinitionManager。
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());

        shiroFilterFactoryBean.setLoginUrl("/index.jsp");
        shiroFilterFactoryBean.setSuccessUrl("/security/mainController");
        shiroFilterFactoryBean.setUnauthorizedUrl("jsps/unauthorized.jsp");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(loadShiroFilterChain());
        return shiroFilterFactoryBean;
    }

    //remember配置
    @Bean
    public SimpleCookie rememberMeCookie() {
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");

        simpleCookie.setHttpOnly(true);
        simpleCookie.setMaxAge(30);

        return simpleCookie;
    }

    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();

        rememberMeManager.setCookie(rememberMeCookie());

        return rememberMeManager;
    }

    /**
     * DefaultAdvisorAutoProxyCreator，Spring的一个bean，由Advisor决定对哪些类的方法进行AOP代理。
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    /**
     * AuthorizationAttributeSourceAdvisor，shiro里实现的Advisor类，
     * 内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor aASA = new AuthorizationAttributeSourceAdvisor();
        aASA.setSecurityManager(securityManager());
        return aASA;
    }

    /**
     * 加载ShiroFilter权限控制规则
     */
    private Map<String, String> loadShiroFilterChain() {
        /**下面这些规则配置最好配置到配置文件中*/
        Map<String, String> filterChainMap = new LinkedHashMap<String, String>();

        filterChainMap.put("/index.jsp", "anon");
        filterChainMap.put("/validatecode.jsp**", "anon");
        filterChainMap.put("/security/toLogin" ,"anon");
        filterChainMap.put("/css/**" ,"anon");
        filterChainMap.put("/scripts/**" ,"anon");
        filterChainMap.put("/audio/**" ,"anon");
        filterChainMap.put("/js/**" ,"anon");
        filterChainMap.put("/pic/**" ,"anon");
        filterChainMap.put("/security/login", "anon");
        filterChainMap.put("/security/toRegister" ,"anon");
        filterChainMap.put("/security/register" ,"anon");
        filterChainMap.put("/security/logout", "logout");

        filterChainMap.put("/**", "user");

        return filterChainMap;
    }

}
